#pragma once

#include <algorithm/analytical_model/algorithm/cuda/math/vector/Common.cuh>
#include <algorithm/shared/utils/GenericVectorMath.h>
#include <utils/config.h>

inline HOT_FUNCTION __host__ __device__ auto make_float3(const float& s) -> float3
{
    return ::make_float3(s, s, s);
}

inline HOT_FUNCTION __host__ __device__ auto make_float3(const float2& a) -> float3
{
    return ::make_float3(a.x, a.y, 0.0F);
}

inline HOT_FUNCTION __host__ __device__ auto make_float3(const float2& a, const float& s) -> float3
{
    return ::make_float3(a.x, a.y, s);
}

inline HOT_FUNCTION __host__ __device__ auto make_float3(const float4&& a) -> float3
{
    return ::make_float3(a.x, a.y, a.z); // discards w
}

inline HOT_FUNCTION __host__ __device__ auto make_float3(const int3&& a) -> float3
{
    return ::make_float3(float(a.x), float(a.y), float(a.z));
}

inline HOT_FUNCTION __host__ __device__ auto operator-(const float3& a) -> float3
{
    return solar::internal::vector::negate(a);
}

inline HOT_FUNCTION __host__ __device__ auto operator+(const float3& a, const float3& b) -> float3
{
    return solar::internal::vector::plus(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto operator+(const float3& a, const float& b) -> float3
{
    return solar::internal::vector::plus(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto operator+(const float& b, const float3& a) -> float3
{
    return solar::internal::vector::plus(a, b);
}

inline HOT_FUNCTION __host__ __device__ void operator+=(float3& a, const float3& b)
{
    solar::internal::vector::plusSelf(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto operator-(const float3& a, const float3& b) -> float3
{
    return solar::internal::vector::minus(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto operator-(const float3& a, const float& b) -> float3
{
    return solar::internal::vector::minus(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto operator-(const float& b, const float3& a) -> float3
{
    return solar::internal::vector::minus(a, b);
}

inline HOT_FUNCTION __host__ __device__ void operator-=(float3& a, const float3& b)
{
    solar::internal::vector::minusSelf(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto operator*(const float3& a, const float3& b) -> float3
{
    return solar::internal::vector::multiply(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto operator*(const float3& a, const float& s) -> float3
{
    return solar::internal::vector::multiply(a, s);
}

inline HOT_FUNCTION __host__ __device__ auto operator*(const float& s, const float3& a) -> float3
{
    return solar::internal::vector::multiply(a, s);
}

inline HOT_FUNCTION __host__ __device__ void operator*=(float3& a, const float& s)
{
    solar::internal::vector::multiplySelf(a, s);
}

inline HOT_FUNCTION __host__ __device__ auto operator/(const float3& a, const float3& b) -> float3
{
#ifdef __CUDA_ARCH__
    return solar::internal::vector::cuda::divide(a, b);
#else
    return solar::internal::vector::divide(a, b);
#endif
}

inline HOT_FUNCTION __host__ __device__ auto operator/(const float3& a, const float& s) -> float3
{
#ifdef __CUDA_ARCH__
    return solar::internal::vector::cuda::divide(a, s);
#else
    return solar::internal::vector::divide(a, s);
#endif
}

inline HOT_FUNCTION __host__ __device__ auto operator/(const float& s, const float3& a) -> float3
{
#ifdef __CUDA_ARCH__
    return solar::internal::vector::cuda::divide(a, s);
#else
    return solar::internal::vector::divide(a, s);
#endif
}

inline HOT_FUNCTION __host__ __device__ void operator/=(float3& a, const float& s)
{
#ifdef __CUDA_ARCH__
    solar::internal::vector::cuda::divideSelf(a, s);
#else
    solar::internal::vector::divideSelf(a, s);
#endif
}

inline HOT_FUNCTION __device__ __host__ auto lerp(const float3& a, const float3& b,
                                                  const float& t) -> float3
{
    return solar::internal::vector::lerp(a, b, t);
}

inline HOT_FUNCTION __device__ __host__ auto clamp(const float3& v, const float& a,
                                                   const float& b) -> float3
{
    return solar::internal::vector::clamp(v, a, b);
}

inline HOT_FUNCTION __device__ __host__ auto clamp(const float3& v, const float3& a,
                                                   const float3& b) -> float3
{
    return solar::internal::vector::clamp(v, a, b);
}

inline HOT_FUNCTION __host__ __device__ auto dot(const float3& a, const float3& b) -> float
{
    return solar::internal::vector::dot(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto length(const float3& v) -> float
{
    return solar::internal::vector::length(v);
}

inline HOT_FUNCTION __host__ __device__ auto rlength(const float3& v) -> float
{
    return solar::internal::vector::rlength(v);
}

inline HOT_FUNCTION __host__ __device__ auto normalize(const float3& v) -> float3
{
    return solar::internal::vector::normalize(v);
}

inline HOT_FUNCTION __host__ __device__ auto floor(const float3& v) -> float3
{
    return solar::internal::vector::floor(v);
}

inline HOT_FUNCTION __host__ __device__ auto reflect(const float3& i, const float3& n) -> float3
{
    return solar::internal::vector::reflect(i, n);
}

inline HOT_FUNCTION __host__ __device__ auto fabs(const float3& v) -> float3
{
    return solar::internal::vector::abs(v);
}

inline HOT_FUNCTION __host__ __device__ auto fmaxf(const float3& a, const float3& b) -> float3
{
    return solar::internal::vector::max(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto fminf(const float3& a, const float3& b) -> float3
{
    return solar::internal::vector::min(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto cross(const float3& a, const float3& b) -> float3
{
    return ::make_float3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
}
